package core.protocol.impl;

import java.rmi.RemoteException;
import java.util.HashSet;
import java.util.Set;

import core.api.ClientAPI;
import core.protocol.ProtocolAD;

public class Bully implements ProtocolAD{

	private final ClientAPI client ;
	int myNumber;
	int coordinator;
	boolean election, ackRecu, newCoordinator;
	private final Set<Integer> neighbors= new HashSet<Integer>();

	public static final int tempo = 5000;

	public Bully(final ClientAPI client){
		super();

		this.client = client;
		try {
			this.myNumber = client.getId();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		election = false;
		ackRecu = false;
		newCoordinator = false;
	}

	@Override
	public void addNeighbour(int i){
		neighbors.add(i);
	}

	@Override
	public void removeNeighbour(int i){
		neighbors.remove(i);
	}

	public boolean testElection(){ return election; }

	public void startElection() {
		System.out.println("Process " + myNumber + " starts election ");
		election = true;
		ackRecu = false;
		newCoordinator = false;

		for(Integer i : neighbors)
			if( i > myNumber )
				try {
					client.callProtocol(myNumber, i,"msgElection",myNumber);
				} catch (RemoteException e2) {
					// TODO Auto-generated catch block
					e2.printStackTrace();
				}
			

		synchronized( this ) {
			if ( !ackRecu )
				try {
					this.wait( tempo );
				} catch (InterruptedException e) {
					System.out.println("ackRecu : " + myNumber + " is not the coordinator");
				}
			if ( ackRecu ) {
				if( !newCoordinator )
					try {
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

			} 
		} 

		// Just to avoid calling neighbors in synchronized state
		if ( !ackRecu ) {
			System.out.println( "***** " + myNumber + " is the coordinator" );
			coordinator = myNumber;
			// O do not need to send coordinator message to process higher than me
			for(Integer i : neighbors)
				if( i < myNumber )
					try {
						client.callProtocol(myNumber, i, "msgCoordinator", coordinator);
					} catch (RemoteException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				
			try {
				client.setMaster(myNumber,myNumber,coordinator);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}

		election = false;
	}

	/*
	 * Thread part
	 */
	@Override
	public void run() {
		startElection();
	}

	/*
	 * Remote part 
	 */
	public void test(Integer procNumber){
		System.out.println("test " + myNumber + " from " + procNumber);
	}

	public synchronized void msgAck(){
		System.out.println("Received ack msg");
		ackRecu = true;
		this.notify();	
	}

	public synchronized void msgCoordinator(Integer proc){
		System.out.println("Received coordinator from " + proc);
		coordinator = proc;
		newCoordinator = true;
		try {
			client.setMaster(myNumber,myNumber,proc);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		this.notify();

	}

	public void msgElection(Integer proc){

		System.out.println("Received election from " + proc);

		try {
			client.callProtocol(myNumber, proc, "msgAck");
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if ( !election ) {
			Thread th = new Thread( this );
			th.start();
		}

	}

	@Override
	public void timeoutProtocol(int dest) {
		//Uselesss for bully
	}

}
